﻿using System;
using System.Collections.Generic;
using System.Linq;
using Summer.Core.Config;
using Summer.Exception;
using Summer.IO;
using Summer.Object;
using Summer.Log;
using System.Collections;


#region License
/*
 *  Copyright (c) Dumuzi.cn 2009 , All rights reserved.
 *  CLR Version: 2.0.50727.4927
 *  NameSpace: Summer.Core.Ioc
 *  Author:Edwin Tai,戴佳顺
 *  Msn:edwin19861218@hotmail.com
 *  QQ: 1961218
 *  Date:2009-11-07 00:24:31
 *  Licensed under the Microsoft Public License (Ms-PL)
 *  This license governs use of the accompanying software. 
 *  If you use the software, you accept this license.
 *  If you do not accept the license, do not use the software.
 */
#endregion

namespace Summer.Core.Ioc
{
    /// <summary>
    /// The configuration to get or format ioc config
    /// </summary>
    public class IocConfiguration : IConfiguration<SummerConfig>
    {
        private static ILog logger = LogFactory.GetLogger(typeof(IocConfiguration), false);
        /// <summary>
        /// Get new instance of root container
        /// </summary>
        /// <param name="iocXml"></param>
        /// <returns></returns>
        public AbstractContextContainer<AbstractContextContainer<IContextObject>> GetRootContainer(string iocXml)
        {
            SummerConfig config = GetConfigFile(iocXml);
            return GetRootContainer(config);
        }
        /// <summary>
        /// Get root container by SummerConfig
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public AbstractContextContainer<AbstractContextContainer<IContextObject>> GetRootContainer(SummerConfig config)
        {
            return (AbstractContextContainer<AbstractContextContainer<IContextObject>>)config.IocConfig.Root.Class.LoadLibrary(); ;
        }
        /// <summary>
        /// Get new instance of containers which contains with its name
        /// </summary>
        /// <param name="iocXml"></param>
        /// <returns></returns>
        public AbstractContextContainer<IContextObject>[] GetContainers(string iocXml)
        {
            SummerConfig config = GetConfigFile(iocXml);
            return GetContainers(config);
        }
        /// <summary>
        /// Get containers by SummerConfig
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public AbstractContextContainer<IContextObject>[] GetContainers(SummerConfig config)
        {
            Summer.Core.Config.SummerConfig.Ioc.ContextObject[] contextObjs = config.IocConfig.Containers.Objects;
            AbstractContextContainer<IContextObject>[] objs = new AbstractContextContainer<IContextObject>[contextObjs.Length];
            for (int i = 0; i < contextObjs.Length; i++)
            {
                objs[i] = (AbstractContextContainer<IContextObject>)contextObjs[i].Class.LoadLibrary();
                objs[i].Name = contextObjs[i].Name;
            }
            return objs;
        }
        /// <summary>
        /// Get containers by SummerConfig array
        /// </summary>
        /// <param name="configs"></param>
        /// <returns></returns>
        public AbstractContextContainer<IContextObject>[] GetContainers(SummerConfig[] configs)
        {
            List<AbstractContextContainer<IContextObject>> lists = new List<AbstractContextContainer<IContextObject>>();
            foreach (SummerConfig config in configs)
            {
                Summer.Core.Config.SummerConfig.Ioc.ContextObject[] contextObjs = config.IocConfig.Containers.Objects;
                for (int i = 0; i < contextObjs.Length; i++)
                {
                    var obj = (AbstractContextContainer<IContextObject>)contextObjs[i].Class.LoadLibrary();
                    obj.Name = contextObjs[i].Name;
                    lists.Add(obj);
                }
            }
            return lists.ToArray();
        }
        /// <summary>
        /// Set contextObject into context 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="iocXml"></param>
        /// <param name="baseContainer"></param>
        public void SetContext(AbstractContextContainer<IContextObject> context, string iocXml, AbstractContextContainer<AbstractContextContainer<IContextObject>> baseContainer)
        {
            SummerConfig config = GetConfigFile(iocXml);
            SetContext(context, config, baseContainer);
        }
        /// <summary>
        /// Set contextObject into context by SummerConfig
        /// </summary>
        /// <param name="context"></param>
        /// <param name="config"></param>
        /// <param name="baseContainer"></param>
        public void SetContext(AbstractContextContainer<IContextObject> context, SummerConfig config, AbstractContextContainer<AbstractContextContainer<IContextObject>> baseContainer)
        {
            var obj = from c in config.IocConfig.Contexts.ContextArray
                      where c.Name == context.Name
                      select c.Objects;
            foreach (var o in obj)
            {
                foreach (var oo in o)
                {
                    SetContextObject(oo, context, baseContainer, config);
                }
            }
            //Add containers into baseContainer
            baseContainer.Add(context.Name, context);
        }
        /// <summary>
        /// Set contextObject into Context if necessary
        /// The contextObject and properities has not intialized now
        /// </summary>
        /// <param name="oo"></param>
        /// <param name="context"></param>
        /// <param name="baseContainer"></param>
        /// <param name="config"></param>
        private void SetContextObject(SummerConfig.Ioc.ContextObject oo, AbstractContextContainer<IContextObject> context, AbstractContextContainer<AbstractContextContainer<IContextObject>> baseContainer, SummerConfig config)
        {
            if (!context.Keys.Contains(oo.Name))
            {
                Func<SummerConfig.Ioc.Property[], IContextObjectProperty[]> funcProp = (SummerConfig.Ioc.Property[] IocPro) =>
                {
                    if (IocPro == null || IocPro.Count() == 0) { return null; }
                    IContextObjectProperty[] array = new IContextObjectProperty[IocPro.Count()];
                    for (int i = 0; i < IocPro.Count(); i++)
                    {
                        IContextObjectProperty pro = (IContextObjectProperty)Activator.CreateInstance(typeof(ContextObjectProperty));
                        //TODO now only can use Value 
                        //object obj=o[i].Value!=null?o[i].Value:(o[i].Lists!=null?(object)o[i].Lists:(o[i].Maps!=null?(object)o[i].Maps:null));
                        object obj = IocPro[i].Value;//string, List or Dictionary
                        pro.Name = IocPro[i].Name;
                        pro.ObjectName = obj;
                        array[i] = pro;
                    }
                    return array;
                };
              
                context.Add(oo.Name, new ContextObject() { Name = oo.Name, Class = oo.Class, Object = null, Property = funcProp(oo.Properties), Singleton = oo.Singleton, Ref = oo.Ref });
            }
            else
            {
                logger.Warn("Load object {0} failure because it exists", oo.Name);
            }
          
        }
     
        #region IConfiguration<SummerConfig> 成员
        /// <summary>
        /// Get the config file data
        /// </summary>
        /// <param name="iocXml"></param>
        /// <returns></returns>
        public SummerConfig GetConfigFile(string iocXml)
        {
            return GetConfigFiles(new string[] { iocXml });
        }
        /// <summary>
        /// Get the config file date by fileList and merge it
        /// </summary>
        /// <param name="configXmls"></param>
        /// <returns></returns>
        public SummerConfig GetConfigFiles(string[] configXmls)
        {
            //load config
            SummerConfig config = new SummerConfig()
            {
                IocConfig = new SummerConfig.Ioc()
                {
                    Containers = new SummerConfig.Ioc.ContainerCollection()
                    {
                        Objects = new SummerConfig.Ioc.ContextObject[] { }
                    },
                    Contexts = new SummerConfig.Ioc.ContextsCollection()
                    {
                        ContextArray = new SummerConfig.Ioc.Context[] { }
                    },
                    Root = new SummerConfig.Ioc.RootContainer()
                }
            };
            //copy all configfile to config
            foreach (string configXml in configXmls)
            {
                SummerConfig s = LoadConfigFile(configXml);
                config.IocConfig.Root = s.IocConfig.Root;
                //ioc-containers load
                config.IocConfig.Containers.Objects = (SummerConfig.Ioc.ContextObject[])config.IocConfig.Containers.Objects.MergeArray(s.IocConfig.Containers.Objects);
                //ioc-contexts load
                config.IocConfig.Contexts.ContextArray = (SummerConfig.Ioc.Context[])config.IocConfig.Contexts.ContextArray.MergeArray(s.IocConfig.Contexts.ContextArray);
            }
            //merge container
            SummerConfig.Ioc.ContextObject[] containers = config.IocConfig.Containers.Objects;
            List<SummerConfig.Ioc.ContextObject> listContainer = new List<SummerConfig.Ioc.ContextObject>();
            for (int i = 0; i < containers.Length; i++)
            {
                var temp = listContainer.Find((SummerConfig.Ioc.ContextObject s) => { return (s.Name == containers[i].Name); });
                if (temp == null)
                {
                    listContainer.Add(containers[i]);
                }
                else
                {
                    logger.Warn("Container object named {0} is missed for the object with the same name exists", temp.Name);
                }
            }
            config.IocConfig.Containers.Objects = listContainer.ToArray();
            //merge context
            SummerConfig.Ioc.Context[] contexts = config.IocConfig.Contexts.ContextArray;
            List<SummerConfig.Ioc.Context> listContext = new List<SummerConfig.Ioc.Context>();
            for (int i = 0; i < contexts.Length; i++)
            {
                var temp = listContext.Find((SummerConfig.Ioc.Context s) => { return (s.Name == contexts[i].Name); });
                if (temp == null)
                {
                    listContext.Add(contexts[i]);
                }
                else
                {
                    ArrayList listObj = new ArrayList();
                    Func<SummerConfig.Ioc.ContextObject, bool> fucExist = delegate(SummerConfig.Ioc.ContextObject o)
                        {
                            foreach (var l in listObj)
                            {
                                if (((SummerConfig.Ioc.ContextObject)l).Name == o.Name)
                                {
                                    logger.Warn("Object named {0} is missed for the object with the same name exists", temp.Name);
                                    return false;
                                }
                            }
                            return true;
                        };
                    temp.Objects.CopyTo(listObj, (object o) =>
                    {
                        SummerConfig.Ioc.ContextObject co = (SummerConfig.Ioc.ContextObject)o;
                        return fucExist(co);

                    });//now all the element in temp(from listContext) is copied to listobj
                    foreach (var v in contexts[i].Objects)
                    {
                        if (fucExist(v))
                        {
                            listObj.Add(v);
                        }
                    }
                    temp.Objects = (SummerConfig.Ioc.ContextObject[])listObj.ToArray(typeof(SummerConfig.Ioc.ContextObject));
                }
            }
            config.IocConfig.Contexts.ContextArray = listContext.ToArray();
            return config;
        }


        public SummerConfig LoadConfigFile(string configXml)
        {
            configXml = configXml.FindPath(new IocException("Config File Not Exist", this.GetType()));
            return new ContextConfigParser().Load(configXml);
        }

        #endregion
    }
}
